Springboot 系列 (10)

您所在的位置:网站首页 springboot websocket端口 Springboot 系列 (10)

Springboot 系列 (10)

2023-04-19 18:26| 来源: 网络整理| 查看: 265

 

WebSocket 是HTML5一种新的协议,实现了浏览器与服务器全双工通信。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,服务端与客户端通过此TCP连接进行实时通信。

Spring 框架下可以使用以下方式来实现 WebSocket 功能:

    (1) Spring 封装的 WebSocket;    (2) STOMP 协议,STOMP 协议是 WebSocket 的一个子协议;    (3) WebSocket 原生注解;

    以上三种方式,都包含在 spring-boot-starter-websocket 包里,前两种使用起来方便,这里暂时不花时间在第三种方式上。

本文使用 Spring 封装的 WebSocket 来实现发送 JSON 消息实例。

1. 开发环境

    Windows版本:Windows 10 Home (20H2)       IntelliJ IDEA (https://www.jetbrains.com/idea/download/):Community Edition for Windows 2020.1.4    Apache Maven (https://maven.apache.org/):3.8.1

    注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。

2. 创建 Spring Boot 基础项目

    项目实例名称:SpringbootExample10    Spring Boot 版本:2.6.6

    创建步骤:

        (1) 创建 Maven 项目实例 SpringbootExample10;        (2) Spring Boot Web 配置;        (3) 导入 Thymeleaf 依赖包;        (4) 配置 jQuery;            具体操作请参考 “Springboot 系列 (2) - 在 Spring Boot 项目里使用 Thymeleaf、JQuery+Bootstrap 和国际化” 里的项目实例 SpringbootExample02,文末包含如何使用 spring-boot-maven-plugin 插件运行打包的内容。

    SpringbootExample10 和 SpringbootExample02 相比,SpringbootExample10 不配置 Bootstrap、模版文件(templates/*.html)和国际化。

3. 导入 WebSocket、FastJson 依赖包

    修改 pom.xml

1 2 ... 3 4 ... 5 6 7 8 org.springframework.boot 9 spring-boot-starter-websocket 10 11 12 13 com.alibaba 14 fastjson 15 1.2.79 16 17 18 ... 19 20 21 ... 22

    在IDE中项目列表 -> SpringbootExample10 -> 点击鼠标右键 -> Maven -> Reload Project

4. 配置 Spring 封装的 WebSocket

    1) 创建 src/main/java/com/example/ws/WSConfig.java 文件

1 package com.example.ws; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.web.socket.config.annotation.EnableWebSocket; 6 import org.springframework.web.socket.config.annotation.WebSocketConfigurer; 7 import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; 8 9 @Configuration 10 @EnableWebSocket 11 public class WSConfig implements WebSocketConfigurer { 12 @Autowired 13 private WSTextHandler wsTextHandler; 14 @Autowired 15 private WSInterceptor wsInterceptor; 16 17 @Override 18 public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 19 registry.addHandler(wsTextHandler, "/websocket") 20 .addInterceptors(wsInterceptor) 21 .setAllowedOrigins("*"); // Allow cross site 22 } 23 24 }

    2) 创建 src/main/java/com/example/ws/WSInterceptor.java 文件

1 package com.example.ws; 2 3 import java.util.Map; 4 5 import org.springframework.stereotype.Component; 6 import org.springframework.http.server.ServerHttpRequest; 7 import org.springframework.http.server.ServerHttpResponse; 8 import org.springframework.web.socket.WebSocketHandler; 9 import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; 10 11 @Component 12 public class WSInterceptor extends HttpSessionHandshakeInterceptor { 13 @Override 14 public boolean beforeHandshake(ServerHttpRequest request, 15 ServerHttpResponse response, 16 WebSocketHandler wsHandler, 17 Map attributes) throws Exception { 18 return super.beforeHandshake(request, response, wsHandler, attributes); 19 } 20 21 @Override 22 public void afterHandshake(ServerHttpRequest request, 23 ServerHttpResponse response, 24 WebSocketHandler wsHandler, 25 Exception ex) { 26 super.afterHandshake(request, response, wsHandler, ex); 27 } 28 29 }

    3) 创建 src/main/java/com/example/ws/WSTextHandler.java 文件

1 package com.example.ws; 2 3 import java.util.List; 4 import java.util.Map; 5 import java.util.HashMap; 6 import java.util.concurrent.CopyOnWriteArrayList; 7 8 import org.springframework.stereotype.Component; 9 import org.springframework.web.socket.CloseStatus; 10 import org.springframework.web.socket.TextMessage; 11 import org.springframework.web.socket.WebSocketMessage; 12 import org.springframework.web.socket.WebSocketSession; 13 import org.springframework.web.socket.handler.TextWebSocketHandler; 14 15 import com.alibaba.fastjson.JSON; 16 import com.alibaba.fastjson.JSONObject; 17 18 @Component 19 public class WSTextHandler extends TextWebSocketHandler { 20 21 private List clientSessions = new CopyOnWriteArrayList(); 22 23 @Override 24 public void afterConnectionEstablished(WebSocketSession session) throws Exception { 25 System.out.println("WSTextHandler -> afterConnectionEstablished(): session.getId() = " + session.getId()); 26 27 clientSessions.add(session); 28 } 29 30 @Override 31 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { 32 System.out.println("WSTextHandler -> afterConnectionClosed(): session.getId() = " 33 + session.getId() + ", status.getCode() = " + status.getCode()); 34 35 clientSessions.remove(session); 36 } 37 38 @Override 39 public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { 40 System.out.println("WSTextHandler -> handleMessage() -> message = " 41 + message.getPayload().toString()); 42 43 Map retMap = new HashMap(); 44 45 JSONObject recvJson = (JSONObject) JSON.parse(message.getPayload().toString()); 46 String opt = recvJson.getString("operation"); 47 48 if ("command".equals(opt)) { 49 50 String msg = recvJson.getString("message"); 51 52 if (msg == null || msg.isEmpty()) { 53 retMap.put("ret", "error"); 54 retMap.put("description", "Invalid command format"); 55 session.sendMessage(new TextMessage(JSON.toJSON(retMap).toString())); 56 return; 57 } 58 59 retMap.put("ret", "data"); 60 retMap.put("message", msg + " (Reply from server)"); 61 session.sendMessage(new TextMessage(JSON.toJSON(retMap).toString())); 62 63 } else if ("close".equals(opt)) { 64 65 retMap.put("ret", "finish"); 66 retMap.put("description", "Finish directly"); 67 session.sendMessage(new TextMessage(JSON.toJSON(retMap).toString())); 68 69 } else { 70 71 retMap.put("ret", "error"); 72 retMap.put("description", "Invalid data format"); 73 session.sendMessage(new TextMessage(JSON.toJSON(retMap).toString())); 74 75 } 76 77 } 78 } 5. 测试实例 (Web 模式)

    1) 创建 src/main/resources/templates/client.html 文件

1 2 3 4 Client 5 6 7 8 9 WebSocket - Client 10 ; 11 12 13 WebSocket url: 14 15 Connect 16 Close 17 18 19 20 Message: 21 22 Send 23 24 25 ; 26 27 28 29 30 31 var globalSocket = null; 32 33 $(document).ready(function() { 34 $("#btn_connect").click(function(e) { 35 connectWebSocket(); 36 }); 37 38 $("#btn_close").click(function(e) { 39 closeWebSocket(); 40 }); 41 42 $("#btn_send").click(function(e) { 43 if (globalSocket != null) { 44 var msg = $("#message").val(); 45 if (msg == '') { 46 alert("Please enter message"); 47 $("#message").focus(); 48 return; 49 } 50 51 var data = { 52 "operation": "command", 53 "message": msg 54 } 55 globalSocket.send(JSON.stringify(data)); 56 } 57 }); 58 }); 59 60 function connectWebSocket() { 61 var wsUrl = $("#ws_url").val(); 62 if (wsUrl == '') { 63 alert("Please enter url"); 64 $("#ws_url").focus(); 65 return; 66 } 67 68 if (globalSocket == null) { 69 $("#result_area").html(''); 70 $("#btn_execute").attr("disabled", "disabled"); 71 72 createWebSocket(wsUrl); 73 } 74 } 75 76 function createWebSocket(url) { 77 if (globalSocket != null || url == '') 78 return; 79 80 console.log("createWebSocket(): url = ", url); 81 globalSocket = new WebSocket(url); 82 globalSocket.onopen = funcWSOpen; 83 globalSocket.onclose = funcWSClose; 84 globalSocket.onerror = funcWSError; 85 globalSocket.onmessage = funcWSMessage; 86 } 87 88 function closeWebSocket() { 89 if (globalSocket != null) { 90 console.log("closeWebSocket(): send close"); 91 globalSocket.send(JSON.stringify({ "operation": "close"})); 92 $("#btn_close").attr("disabled", "disabled"); 93 $("#message_area").css("display", "none"); 94 } 95 } 96 97 function funcWSOpen(e) { 98 console.log("funcWSOpen(): ", e); 99 100 $("#message_area").css("display", ""); 101 $("#btn_close").removeAttr("disabled"); 102 $("#btn_close").css("display", ""); 103 104 $("#result_area").append("WSOpen: Connected"); 105 } 106 107 function funcWSClose(e) { 108 console.log("funcWSClose(): ", e); 109 110 $("#result_area").append("WSClose: Close"); 111 $("#btn_execute").removeAttr("disabled"); 112 $("#btn_close").css("display", "none"); 113 globalSocket = null; 114 } 115 116 function funcWSError(e) { 117 console.error("funcWSError(): ", e); 118 119 $("#result_area").append("WSError: Error"); 120 $("#btn_execute").removeAttr("disabled"); 121 $("#btn_close").css("display", "none"); 122 globalSocket = null; 123 } 124 125 function funcWSMessage(e) { 126 console.log("funcWSMessage(): e.data = ", e.data); 127 128 var dataObj = JSON.parse(e.data); 129 if (dataObj['ret'] == "data") { 130 $("#result_area").append("WSMessage: " + dataObj['message'] + ""); 131 } else if (dataObj['ret'] == "finish") { 132 console.log("funcWSMessage(): ", dataObj['description']) 133 $("#result_area").append("WSMessage: " + dataObj['description'] + ""); 134 globalSocket.close(3009) 135 } else if (dataObj['ret'] == "error") { 136 console.log("funcWSMessage(): ", dataObj['description']); 137 $("#result_area").append("WSMessage: " + dataObj['description'] + ""); 138 } else { 139 $("#result_area").append("WSMessage: Invalid data format"); 140 } 141 } 142 143 144

    2) 修改 src/main/java/com/example/controller/IndexController.java 文件

1 package com.example.controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.ResponseBody; 6 7 @Controller 8 public class IndexController { 9 @ResponseBody 10 @RequestMapping("/test") 11 public String test() { 12 return "Test Page"; 13 } 14 15 @RequestMapping("/client") 16 public String client() { 17 return "client"; 18 } 19 20 }

    访问 http://localhost:9090/client,JAR 方式运行时,页面上 WebSocket url 是 ws://localhost:9090/websocket,点击 Connect 按钮测试。        注:在 Tomcat 容器里以 WAR 方式运行时,页面上 WebSocket url 是 ws://localhost:8080/SpringbootExample10/websocket (Tomcat 默认端口是 8080)。

--------------------------------------

示例代码:https://gitee.com/slksm/public-codes/tree/master/demos/springboot-series/SpringbootExample10

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3